package com.gxar.file.server.base.sdk;

import java.io.*;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;
import java.util.Vector;
import java.util.concurrent.ConcurrentHashMap;

import com.gxar.file.server.base.common.enums.TargetChannelEnum;
import com.gxar.file.server.base.common.enums.ProductEnum;
import com.gxar.file.server.base.common.utils.FilePathUtils;
import com.gxar.file.server.db.model.ProductEntity;
import com.gxar.file.server.db.respository.ProductRepository;
import com.jcraft.jsch.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletResponse;

/**
 * nas文件管理
 *
 * @author Linsy
 */
@Slf4j
@Component("nasHelper")
@RequiredArgsConstructor
public class NasHelper {

    private final static Map<String, ChannelSftp> CHANNEL_SFTP_CLIENT_MAP = new ConcurrentHashMap<>();

    private final static Map<String, ProductEntity> PRODUCT_MAP = new ConcurrentHashMap<>();
    private final static Integer PORT = 22;

    private final ProductRepository productRepository;

    private synchronized ChannelSftp getChannelSftpClient(String productName) {
        ChannelSftp channelSftp = CHANNEL_SFTP_CLIENT_MAP.get(productName);
        if (channelSftp != null) {
            return channelSftp;
        } else {
            ProductEntity productEntity = productRepository.findByNameAndChannel(productName, TargetChannelEnum.NAS.getCode());
            if (Objects.nonNull(productEntity)) {
                PRODUCT_MAP.put(productName, productEntity);
                channelSftp = connect(productEntity.getEndpoint(), PORT, productEntity.getAccessKey(), productEntity.getAccessSecret());
                CHANNEL_SFTP_CLIENT_MAP.put(productName, channelSftp);
                return channelSftp;
            } else {
                return null;
            }
        }
    }

    /**
     * 连接sftp服务器
     *
     * @param host     主机
     * @param port     端口
     * @param username 服务器用户名
     * @param password 密码
     * @return sftp
     */
    private ChannelSftp connect(String host, Integer port, String username,
                                String password) {
        ChannelSftp sftp = null;
        try {
            JSch jsch = new JSch();
            jsch.getSession(username, host, port);
            Session sshSession = jsch.getSession(username, host, port);
            log.debug("Session created.");
            sshSession.setPassword(password);
            Properties sshConfig = new Properties();
            sshConfig.put("StrictHostKeyChecking", "no");
            sshSession.setConfig(sshConfig);
            sshSession.connect();
            Channel channel = sshSession.openChannel("sftp");
            channel.connect();
            sftp = (ChannelSftp) channel;
            log.debug("登录成功: Connected to " + host + ".");
        } catch (Exception ignored) {

        }
        return sftp;
    }

    /**
     * 上传文件
     *
     * @param productEnum 产品枚举
     * @param directory   上传的目录
     * @param inputStream 要上传的文件
     * @return 是否上传成功
     */
    public Boolean uploadFile(ProductEnum productEnum, String directory, InputStream inputStream) {
        ChannelSftp sftp = getChannelSftpClient(productEnum.getName());
        if (Objects.isNull(sftp)) {
            log.error("[{}]该产品没有NAS客户端", productEnum.getName());
            return false;
        }
        ProductEntity productEntity = PRODUCT_MAP.get(productEnum.getName());
        if (Objects.isNull(productEntity) || StringUtils.isBlank(productEntity.getBucketName())) {
            log.error("[{}]该产品没有NAS文件夹", productEnum.getName());
            return false;
        }
        try {
            String path = "/" + productEntity.getBucketName() + "/" + FilePathUtils.getPath(directory);
            Boolean filePathExists = filePathExists(sftp, path);
            if (!filePathExists) {
                sftp.mkdir(path);
            }
            sftp.cd(path);
            sftp.put(inputStream, FilePathUtils.getName(directory));
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    log.error("关闭流异常");
                }
            }
        }
        return true;
    }

    /**
     * 下载文件
     *
     * @param productEnum 产品枚举
     * @param fileKey     下载目录
     * @param response    下载的文件
     * @return 错误信息
     */
    public String downloadFile(ProductEnum productEnum, String fileKey, HttpServletResponse response) {
        ChannelSftp sftp = getChannelSftpClient(productEnum.getName());
        if (Objects.isNull(sftp)) {
            log.error("[{}]该产品没有NAS客户端", productEnum.getName());
            return "产品没有NAS客户端";
        }
        ProductEntity productEntity = PRODUCT_MAP.get(productEnum.getName());
        if (Objects.isNull(productEntity) || StringUtils.isBlank(productEntity.getBucketName())) {
            log.error("[{}]该产品没有NAS目录", productEnum.getName());
            return "产品没有NAS目录";
        }
        OutputStream data = null;
        InputStream is = null;
        try {
            String path = "/" + productEntity.getBucketName() + "/" + fileKey;
            Boolean pathExists = filePathExists(sftp, path);
            if (!pathExists) {
                return "文件不存在";
            }
            sftp.cd("/" + productEntity.getBucketName() + "/" + FilePathUtils.getPath(fileKey));
            is = sftp.get(FilePathUtils.getName(fileKey));
            data = response.getOutputStream();
            // 5M一次读写
            byte[] buffer = new byte[1024 * 1024 * 5];
            int len;
            while ((len = is.read(buffer)) != -1) {
                data.write(buffer, 0, len);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (null != is) {
                try {
                    is.close();
                } catch (IOException e) {
                    log.error("流关闭异常:[{}]", fileKey);
                }
            }
            if (null != data) {
                try {
                    data.close();
                } catch (IOException e) {
                    log.error("字节数组流关闭异常:[{}]", fileKey);
                }
            }
        }
        return null;
    }

    /**
     * 关闭连接
     *
     * @param sftp sftp
     */
    public static void disconnect(ChannelSftp sftp) {
        if (sftp == null) {
            return;
        }
        if (sftp.isConnected()) {
            try {
                if (sftp.getSession() != null && sftp.getSession().isConnected()) {
                    sftp.getSession().disconnect();
                }
            } catch (JSchException e) {
                throw new RuntimeException(e);
            }
            sftp.disconnect();
        }
    }

    /**
     * 校验文件是否存在
     *
     * @param sftp     sftp
     * @param filePath 文件路径
     * @return 是否存在
     */
    private Boolean filePathExists(ChannelSftp sftp, String filePath) {
        SftpATTRS attrs = null;
        try {
            attrs = sftp.stat(filePath);
        } catch (SftpException ignored) {

        }
        return attrs != null;
    }

    public String getFileUrl(ProductEnum product, String name) {
        String productName = product.getName();
        ProductEntity nasProduct = productRepository.findByNameAndChannel(productName, TargetChannelEnum.NAS.ordinal());
        String endpoint = nasProduct.getEndpoint();
        return "http://" + endpoint + ":5005/" + nasProduct.getBucketName() + "/" + name;
    }
}